package com.geekgo.lucenepp.core.config.index.impl;

import com.alibaba.fastjson.JSONObject;
import com.geekgo.lucenepp.core.config.datasource.LuceneDataSource;
import com.geekgo.lucenepp.core.config.index.IndexFactory;
import com.geekgo.lucenepp.model.Field;
import com.geekgo.lucenepp.model.Page;
import com.geekgo.lucenepp.util.DateUtil;
import com.geekgo.lucenepp.util.QueryUtil;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.IntPoint;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.search.highlight.QueryScorer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 查询索引
 *
 * @author HeHaoyang
 * @create 2017-06-13 20:05
 **/
public class QueryIndex implements IndexFactory {

    private final Logger log = LoggerFactory.getLogger(QueryIndex.class);

    /**
     * 查询所有记录,模糊查询
     * @param dataSource
     * @param keyword
     * @param clazz
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> findList(LuceneDataSource dataSource, String keyword, Class<T> clazz) {
        try {
            long startTime = DateUtil.getDate();
            IndexSearcher searcher = new IndexSearcher(dataSource.getIndexReader());
            Query query = new MultiFieldQueryParser(QueryUtil.getQueryFields(dataSource.getXmlDem()),dataSource.getAnalyzer()).parse(keyword);
            TopDocs rs = searcher.search(query,Integer.MAX_VALUE,QueryUtil.sortFields(dataSource.getXmlDem()));
            QueryScorer scorer = new QueryScorer(query);
            DateUtil.timeConsuming("查询内核耗时", startTime);
            long date = DateUtil.getDate();
            List<T> dataHandle = dataHandle(searcher, rs.scoreDocs, scorer,dataSource,clazz);
            DateUtil.timeConsuming("查询数据整理耗时", date);
            return dataHandle;
        } catch (Exception e) {
            log.error("查询索引-error:",e);
            return null;
        }
    }

    @Override
    public <T> Page<T> findPageList(LuceneDataSource dataSource, String keyword, int pageNumber, int pageSize, Class<T> clazz) {
        try {
            long startTime = DateUtil.getDate();
            IndexSearcher searcher = new IndexSearcher(dataSource.getIndexReader());
            Query query = new MultiFieldQueryParser(QueryUtil.getQueryFields(dataSource.getXmlDem()),dataSource.getAnalyzer()).parse(keyword);
            int start = (pageNumber - 1) * pageSize;
            TopFieldCollector create = TopFieldCollector.create(QueryUtil.sortFields(dataSource.getXmlDem()),start + pageSize,false,false,false);
            searcher.search(query,create);
            int totalHits = create.getTotalHits();
            ScoreDoc[] scoreDocs = create.topDocs(start,pageSize).scoreDocs;
            QueryScorer scorer = new QueryScorer(query);
            DateUtil.timeConsuming("查询内核耗时", startTime);
            long date = DateUtil.getDate();
            List<T> dataHandle = dataHandle(searcher, scoreDocs, scorer,dataSource,clazz);
            int totalPage = (int) (totalHits / pageSize);
            if (totalHits % pageSize != 0) {
                totalPage++;
            }
            DateUtil.timeConsuming("查询数据整理耗时", date);
            return new Page<T>(dataHandle, pageNumber, pageSize, totalPage, totalHits);
        } catch (Exception e) {
            log.error("查询索引-error:",e);
            return null;
        }

    }

    /**
     * 默认根据权重高低进行排序
     * @param dataSource
     * @param query
     * @param clazz
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> findRangeList(LuceneDataSource dataSource, Query query, Class<T> clazz) {
        try {
            long startTime = DateUtil.getDate();
            IndexSearcher searcher = new IndexSearcher(dataSource.getIndexReader());
            TopDocs rs = searcher.search(query,Integer.MAX_VALUE);
            QueryScorer scorer = new QueryScorer(query);
            DateUtil.timeConsuming("查询内核耗时", startTime);
            long date = DateUtil.getDate();
            List<T> dataHandle = dataHandle(searcher, rs.scoreDocs, scorer, dataSource, clazz);
            DateUtil.timeConsuming("查询数据整理耗时", date);
            return dataHandle;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new ArrayList<>();
    }

    /**
     * 根据配置文件里面字段的sort进行排序
     * @param dataSource
     * @param query
     * @param clazz
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> findRangeListSort(LuceneDataSource dataSource, Query query, Class<T> clazz) {
        try {
            long startTime = DateUtil.getDate();
            IndexSearcher searcher = new IndexSearcher(dataSource.getIndexReader());
            TopDocs rs = searcher.search(query,Integer.MAX_VALUE,QueryUtil.sortFields(dataSource.getXmlDem()));
            QueryScorer scorer = new QueryScorer(query);
            DateUtil.timeConsuming("查询内核耗时", startTime);
            long date = DateUtil.getDate();
            List<T> dataHandle = dataHandle(searcher, rs.scoreDocs, scorer, dataSource, clazz);
            DateUtil.timeConsuming("查询数据整理耗时", date);
            return dataHandle;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new ArrayList<>();
    }

    @Override
    public <T> Page<T> findRangePageList(LuceneDataSource dataSource, Query query, int pageNumber, int pageSize, Class<T> clazz) {
        try {
            long start_time = DateUtil.getDate();
            IndexSearcher searcher = new IndexSearcher(dataSource.getIndexReader());
            int start = (pageNumber - 1) * pageSize;
            TopFieldCollector create = TopFieldCollector.create(QueryUtil.sortFields(dataSource.getXmlDem()),start + pageSize,false,false,false);
            searcher.search(query,create);
            int totalHits = create.getTotalHits();
            ScoreDoc[] scoreDocs = create.topDocs(start,pageSize).scoreDocs;
            QueryScorer scorer = new QueryScorer(query);
            DateUtil.timeConsuming("查询内核耗时", start_time);
            long date = DateUtil.getDate();
            List<T> dataHandle = dataHandle(searcher, scoreDocs, scorer,dataSource,clazz);
            int totalPage = (int) (totalHits / pageSize);
            if (totalHits % pageSize != 0) {
                totalPage++;
            }
            DateUtil.timeConsuming("查询数据整理耗时", date);
            return new Page<T>(dataHandle, pageNumber, pageSize, totalPage, totalHits);
        } catch (Exception e) {
            log.error("分页检索-error:",e);
        }
        return new Page<>();
    }

    /**
     * 数据整理
     * @param searcher
     * @param scoreDocs
     * @param scorer
     * @param dataSource
     * @param clazz
     * @param <T>
     * @return
     * @throws IOException
     */
    private  <T> List<T> dataHandle(IndexSearcher searcher, ScoreDoc[] scoreDocs, QueryScorer scorer, LuceneDataSource dataSource, Class<T> clazz) throws IOException {
        List<T> list = new ArrayList<>();
        Highlighter highlighter = QueryUtil.getHighlighter(dataSource, scorer);
        for (int i = 0; i < scoreDocs.length; i++) {
            JSONObject object = new JSONObject();
            Document doc = searcher.doc(scoreDocs[i].doc);
            //测试start
            //System.err.println(doc.get("shortName")+"打分："+scoreDocs[i].score);
            //测试end
            List<Field> xmlDem = dataSource.getXmlDem();
            for (int j = 0; j < xmlDem.size(); j++) {
                Field field = xmlDem.get(j);
                String name = field.getName();
                String value = doc.get(name);
                if(dataSource.getHighlight() && field.isQuery()){
                    //高亮设置
                    String bestFragment = null;
                    try {
                        bestFragment = highlighter.getBestFragment(dataSource.getAnalyzer().tokenStream(name, value), value);
                    } catch (InvalidTokenOffsetsException e) {
                        log.error("高亮异常:", e);
                    }
                    if(bestFragment != null){
                        object.put(name, bestFragment);
                    }else{
                        object.put(name, value);
                    }
                }else{
                    object.put(name, value);
                }
            }
            list.add(JSONObject.toJavaObject(object,clazz));
        }
        return list;
    }
}
